package controller

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"parking-lot-go-dingding/config"
	"parking-lot-go-dingding/model"
	"time"

	"github.com/gin-gonic/gin"
)

type Result struct {
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

func Success(c *gin.Context, data interface{}) {
	c.JSON(http.StatusOK, Result{
		Code: http.StatusOK,
		Msg:  "success",
		Data: data,
	})
}

func Fail(c *gin.Context, msg string, code ...int) {
	statusCode := http.StatusBadRequest
	if len(code) > 0 {
		statusCode = code[0]
	}

	c.JSON(http.StatusOK, Result{
		Code: statusCode,
		Msg:  msg,
		Data: "",
	})
}

// 获取accesstoken
func AccessToken(corpId string) (string, error) {
	var err error

	exists, err := model.RedisClient.Exists("ding_talk_access_token_" + corpId).Result()
	if err != nil {
		return "", err
	}

	if exists == 1 {
		accessToken, err := model.RedisClient.Get("ding_talk_access_token_" + corpId).Result()
		if err != nil {
			return "", err
		}
		return accessToken, nil
	} else {

		//获取配置信息
		dingdingSection := config.Cfg.Section("DINGDING")
		suiteSecret := dingdingSection.Key("CLIENT_SECRET").String()
		suiteKey := dingdingSection.Key("CLIENT_ID").String()

		//缓存取ticket
		suiteTicket, err := model.RedisClient.Get("ding_talk_suite_ticket").Result()
		if err != nil {
			return "", err
		}

		//url 请求access_token
		url := "https://api.dingtalk.com/v1.0/oauth2/corpAccessToken"

		// 请求的数据
		requestData := map[string]interface{}{
			"suiteKey":    suiteKey,
			"suiteSecret": suiteSecret,
			"authCorpId":  corpId,
			"suiteTicket": suiteTicket,
		}
		responseBody, err := PostJsonRequest(url, requestData)

		//数据解析
		responseStruct := struct {
			ExpireIn    int    `json:"expireIn"`
			AccessToken string `json:"accessToken"`
		}{}

		err = json.Unmarshal(responseBody, &responseStruct)
		if err != nil {
			return "", err
		}

		//数据入缓存
		model.RedisClient.Set("ding_talk_access_token_"+corpId, responseStruct.AccessToken, time.Duration((responseStruct.ExpireIn-10))*time.Second)

		return responseStruct.AccessToken, nil
	}
}

func PostJsonRequest(url string, requestBody interface{}) ([]byte, error) {

	// 将请求数据转换为 JSON 格式
	jsonBody, err := json.Marshal(requestBody)
	if err != nil {
		return nil, fmt.Errorf("Error marshalling request body: %v", err)
	}

	// 发起 POST 请求
	resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonBody))
	if err != nil {
		return nil, fmt.Errorf("Error making POST request: %v", err)
	}
	defer resp.Body.Close()

	// 读取响应
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Error reading response body: %v", err)
	}
	fmt.Println(string(respBody))
	return respBody, nil
}

// bool类型转换
func BoolToInt(data bool) int {
	if data {
		return 1
	}
	return 0
}

// 字符串格式转换
func StringToDate(timeStr, format string) (time.Time, error) {
	// 获取上海时区
	loc, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		return time.Time{}, err
	}

	// 将字符串转换为 time.Time 类型
	t, err := time.ParseInLocation(format, timeStr, loc)
	if err != nil {
		return time.Time{}, err
	}

	return t, nil
}

// 获取当前环境
func GetSetting() bool {

	// 读取配置文件
	dbSection := config.Cfg.Section("SYSTEM")
	setting := dbSection.Key("SETTING").String()
	if setting == "develop" {
		return false
	}
	return true
}

func GetAgentId(corpId string) (int, error) {

	dingdingSection := config.Cfg.Section("DINGDING")
	suiteKey := dingdingSection.Key("CLIENT_ID").String()

	//缓存取ticket
	suiteTicket, err := model.RedisClient.Get("ding_talk_suite_ticket").Result()
	if err != nil {
		return 0, errors.New("获取suite_ticket失败")
	}

	//时间戳
	timeStamp := MillisecondTimestamp()

	//签名
	sign, err := Sign()

	url := fmt.Sprintf("https://oapi.dingtalk.com/service/get_auth_info?accessKey=%s&timestamp=%s&suiteTicket=%s&signature=%s", suiteKey, timeStamp, suiteTicket, sign)

	requestData := map[string]string{
		"suite_key":   suiteKey,
		"auth_corpid": corpId,
	}
	responseBody, err := PostJsonRequest(url, requestData)
	if err != nil {
		return 0, errors.New(fmt.Sprintf("请求失败%s", err.Error()))
	}

	type ResponseData struct {
		AuthInfo struct {
			Agent []struct {
				AgentID int `json:"agentid"`
			} `json:"agent"`
		} `json:"auth_info"`
	}
	var responseData ResponseData
	if err := json.Unmarshal(responseBody, &responseData); err != nil {
		return 0, errors.New("解析响应结果失败")
	}

	agentId := responseData.AuthInfo.Agent[0].AgentID

	return agentId, nil
}

// 毫秒时间戳
func MillisecondTimestamp() string {
	return fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond))
}

// 签名
func Sign() (string, error) {
	timeStamp := MillisecondTimestamp()

	dingdingSection := config.Cfg.Section("DINGDING")
	suiteSecret := dingdingSection.Key("CLIENT_SECRET").String()

	//缓存取ticket
	suiteTicket, err := model.RedisClient.Get("ding_talk_suite_ticket").Result()
	if err != nil {
		return "", errors.New("获取suite_ticket失败")
	}

	signStr := timeStamp + "\n" + suiteTicket

	// 计算签名
	signature := calculateSignature(signStr, suiteSecret)

	// 对签名结果进行Base64编码
	encodedSignature := base64.StdEncoding.EncodeToString([]byte(signature))

	// 对签名参数进行URL编码
	encodedSignature = url.QueryEscape(encodedSignature)
	return encodedSignature, nil
}

func calculateSignature(signStr, secret string) string {
	// 将密钥解码为字节数组
	key := []byte(secret)

	// 创建HMAC对象
	h := hmac.New(sha256.New, key)

	// 写入消息
	h.Write([]byte(signStr))

	// 计算HMAC值
	signature := h.Sum(nil)

	return string(signature)
}
